package com.flying.invest.strategy.service;

import java.util.Date;
import java.util.List;

import com.flying.common.annotation.DaoCreate;
import com.flying.common.annotation.DaoQuery;
import com.flying.common.annotation.DaoRemove;
import com.flying.common.annotation.DaoUpdate;
import com.flying.common.annotation.Transaction;
import com.flying.common.util.Codes;
import com.flying.common.util.DateUtils;
import com.flying.common.util.DateUtils.Field;
import com.flying.framework.annotation.MethodInfo;
import com.flying.framework.annotation.Param;
import com.flying.framework.annotation.Service;
import com.flying.framework.exception.AppException;
import com.flying.framework.model.ActionResult;
import com.flying.framework.model.ModelFactory;
import com.flying.framework.model.QueryResult;
import com.flying.framework.service.AbstractService;
import com.flying.invest.invest.TransactionType;
import com.flying.invest.invest.model.Investment;
import com.flying.invest.invest.model.InvestmentSuggestion;
import com.flying.invest.invest.model.InvestmentTransaction;
import com.flying.invest.invest.service.InvestmentService;
import com.flying.invest.invest.service.InvestmentSuggestionService;
import com.flying.invest.invest.service.InvestmentTransactionService;
import com.flying.invest.strategy.model.Strategy;
import com.flying.invest.strategy.model.StrategyTrend;
import com.flying.invest.strategy.model.StrategyTrend.CommissionType;
import com.flying.invest.strategy.model.StrategyTrend.IntervalType;
import com.flying.invest.strategy.model.StrategyTrendDetail;

@Service(value="StrategyTrendService", desc="趋势策略")
public class StrategyTrendService extends AbstractService implements IStrategyService, ITransactionCallbackService{
	private InvestmentService investService;
    private InvestmentTransactionService transactionService;
    private StrategyService strategyService;
    private InvestmentSuggestionService suggestionService;
    private StrategyTrendDetailService trendDetailService;
    private StrategyTrendTransactionService trendTransactionService;
    
    public void setInvestmentService(InvestmentService s) {
    	this.investService = s;
    }
    public void setStrategyService(StrategyService s) {
    	this.strategyService = s;
    }
    public void setInvestmentTransactionService(InvestmentTransactionService s) {
    	this.transactionService = s;
    }
    public void setInvestmentSuggestionService(InvestmentSuggestionService s) {
    	this.suggestionService = s;
    }
    public void setStrategyTrendDetailService(StrategyTrendDetailService s) {
    	this.trendDetailService = s;
    }
    public void setStrategyTrendTransactionService(StrategyTrendTransactionService s) {
    	this.trendTransactionService = s;
    }
        
	@MethodInfo("新增")
	@DaoCreate(entity=StrategyTrend.ENTITY_NAME)
	public StrategyTrend create(
			@Param(value=StrategyTrend.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			@Param(value=StrategyTrend.PRICE_BENCHMARK, required=true,	desc="基准价格") Double price_benchmark,
			@Param(value=StrategyTrend.PRICE_MAX, required=true,	desc="价格上限") Double price_max,
			
			@Param(value=StrategyTrend.PRICE_INTERVAL, required=true,	desc="网格价格") Double price_interval,
			@Param(value=StrategyTrend.PRICE_INTERVAL_TYPE, required=true,	desc="区间类型", enumClass=IntervalType.class) Integer price_interval_type,
			@Param(value=StrategyTrend.AMOUNT, required=false,	desc="委托金额") Double amount,
			@Param(value=StrategyTrend.QUANTITY, required=false,	desc="委托数量") Double quantity,
			@Param(value=StrategyTrend.COMMISSION_TYPE, required=true,	desc="委托类型", enumClass=CommissionType.class) Integer commission_type
		) {
		return null;
	}

	@SuppressWarnings("unchecked")
	@Override
	public StrategyTrend createSub(Long strategy_id) {
		final Double rate = 0.03;
		Strategy s = this.strategyService.findById(strategy_id);
		Investment v = this.investService.findById(s.getInvestId());
		return this.create(strategy_id, v.getPrice(), v.getPrice() * (1+0.4), 
				rate, IntervalType.RATE.value, 
				0.0, 100.0, CommissionType.QUANTITY.value);
	}

	@MethodInfo("修改")
	@DaoUpdate(entity=StrategyTrend.ENTITY_NAME)
	public ActionResult update(
			@Param(value=StrategyTrend.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			@Param(value=StrategyTrend.PRICE_BENCHMARK, required=true,	desc="基准价格") Double price_benchmark,
			@Param(value=StrategyTrend.PRICE_MAX, required=true,	desc="价格上限") Double price_max,
			
			@Param(value=StrategyTrend.PRICE_INTERVAL, required=true,	desc="网格价格") Double price_interval,
			@Param(value=StrategyTrend.PRICE_INTERVAL_TYPE, required=true,	desc="区间类型", enumClass=IntervalType.class) Integer price_interval_type,
			@Param(value=StrategyTrend.AMOUNT, required=false,	desc="委托金额") Double amount,
			@Param(value=StrategyTrend.QUANTITY, required=false,	desc="委托数量") Double quantity,
			@Param(value=StrategyTrend.COMMISSION_TYPE, required=true,	desc="委托类型", enumClass=CommissionType.class) Integer commission_type
		) {
		
		return null;
	}
	
	public ActionResult config(
			@Param(value=StrategyTrend.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id,
			@Param(value=StrategyTrend.PRICE_BENCHMARK, required=true,	desc="基准价格", min="0.0001") Double price_benchmark,
			@Param(value=StrategyTrend.PRICE_MAX, required=true,	desc="价格上限") Double price_max,
			
			@Param(value=StrategyTrend.PRICE_INTERVAL, required=true,	desc="网格价格", min="0.0001") Double price_interval,
			@Param(value=StrategyTrend.PRICE_INTERVAL_TYPE, required=true,	desc="区间类型", enumClass=IntervalType.class) Integer price_interval_type,
			@Param(value=StrategyTrend.AMOUNT, required=false,	desc="委托金额") Double amount,
			@Param(value=StrategyTrend.QUANTITY, required=false,	desc="委托数量") Double quantity,
			@Param(value=StrategyTrend.COMMISSION_TYPE, required=true,	desc="委托类型", enumClass=CommissionType.class) Integer commission_type
			
		) {
		if(price_max < price_benchmark) {
			throw new AppException("0101", "价格上限小于于基准价格！");
		}
		if(commission_type == CommissionType.AMOUNT.value() && (amount == null || amount == 0.0)) {
			throw new AppException("0101", "委托金额不能空！");
		}else if(commission_type == CommissionType.QUANTITY.value() && (quantity == null || quantity == 0.0)) {
			throw new AppException("0101", "委托数量不能空！");
		}
		try {
			this.update(strategy_id, price_benchmark, price_max, price_interval, price_interval_type, amount, quantity, commission_type);
		} catch (Exception e) {
			this.create(strategy_id, price_benchmark, price_max, price_interval, price_interval_type, amount, quantity, commission_type);
		}
		this.flushDetails(strategy_id);
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}

	@MethodInfo("删除")
	@Transaction
	@DaoRemove(entity=StrategyTrend.ENTITY_NAME)
	public ActionResult remove(
			@Param(value=StrategyTrend.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id) {
		this.trendDetailService.removeByStrategyId(strategy_id);
		return null;
	}

	@MethodInfo("搜索")
	@DaoQuery(entity=StrategyTrend.ENTITY_NAME, pageable=true, modelClass=StrategyTrend.class)
	public QueryResult<StrategyTrend> findAll(
			@Param(value=Codes.PAGE_NUM, 	required=false, desc="分页页号，默认0") int page,
			@Param(value=Codes.PAGE_SIZE, 	required=false, desc="分页大小，默认10") int rows) {
		return null;
	}

	@SuppressWarnings("unchecked")
	@MethodInfo("查询")
	@DaoQuery(entity=StrategyTrend.ENTITY_NAME, single=true, throwsNotFoundException=true )
	public StrategyTrend findById(
			@Param(value=StrategyTrend.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id
		) {
		return null;
	}
	
	@Override
	@Transaction
	public ActionResult transactionCallback(Long strategy_id, Long transaction_id, Integer transaction_status, 
			Integer transaction_type, Double quantity, Double price, Double amount) {
		if(transaction_status != InvestmentTransaction.Status.SUCCESS.value()) {
			return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
		}
		InvestmentTransaction transaction = this.transactionService.findById(transaction_id);
		Strategy s = this.strategyService.findById(strategy_id);
		StrategyTrend trend = this.findById(strategy_id);
		if(transaction.getSuggestionId() != null) {
			Double dis_sell_price = trend.getPriceIntervalType() == IntervalType.AMOUNT.value()?
					trend.getPriceInterval(): price * trend.getPriceInterval();
					
			InvestmentSuggestion suggestion = this.suggestionService.findById(transaction.getSuggestionId());
			StrategyTrendDetail d = this.trendDetailService.findById(Long.parseLong(suggestion.getExtra()));
			if(transaction_type == TransactionType.BUY.value()) {
				Double suggest_sell_price = price - dis_sell_price;
				Double suggest_buy_price = price + dis_sell_price;
				Double price_interval = suggest_buy_price / price;
				
				Invest buy = new Invest(trend.getAmount(), suggest_buy_price, 100.0);
				if(trend.getCommissionType() == CommissionType.QUANTITY.value()) {
					buy.setQuantity(trend.getQuantity());
					buy.setAmount(buy.getQuantity() * suggest_buy_price);
				}
				//更新买入信息和当前档卖出价格
				this.trendDetailService.updateBuyInfo(d.getDetailId(), price, quantity, amount, transaction_id, new Date(), 
						suggest_sell_price, price, 0.0);
				//创建下一档买入信息
				this.trendDetailService.create(s.getUserId(), s.getTypeId(), s.getInvestId(), strategy_id, 
						s.getBatchNum() + 1, price_interval, suggest_buy_price, buy.getAmount(), buy.getQuantity());
				this.strategyService.updateExecutions(strategy_id, new Date(), s.getBatchNum() + 1);
			} else if(transaction_type == TransactionType.SELL.value()) {
				//更新卖出信息
				Double profit = price * quantity - d.getBuyAmount();
				this.trendDetailService.updateSellInfo(d.getDetailId(), price, quantity, amount, price, profit);
			}
			this.trendTransactionService.create(transaction_id, d == null? -1:d.getDetailId(), s.getUserId(), s.getTypeId(), s.getInvestId(), strategy_id, 
					transaction_type, amount, quantity, price, new Date());
		} else {
			//非策略建议卖出（暂不处理非建议买入），
			if(transaction_type == TransactionType.SELL.value()) {
				List<StrategyTrendDetail> list = this.trendDetailService.findByStrategyId(strategy_id).getRows();
				StrategyTrendDetail ld = null;
				for(int i=list.size() - 1; i>= 0; i--) {
					StrategyTrendDetail d = list.get(i);
					if(d.getBuyQuantity() > 0 && d.getSellQuantity() == null) {	//有买入，没卖出
						Double dquantity = Math.min(d.getBuyQuantity(), quantity);
						quantity -= dquantity;
						Double profit = price * dquantity - d.getBuyAmount();
						this.trendDetailService.updateSellInfo(d.getDetailId(), price, dquantity, price * dquantity, price, profit);
						ld = d;
						if(quantity <= 0) break;
					}
				}
				this.trendTransactionService.create(transaction_id, ld == null? -1:ld.getDetailId(), s.getUserId(), s.getTypeId(), s.getInvestId(), strategy_id, 
						transaction_type, amount, quantity, price, new Date());
			}
		}
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}
	
	@Override
	public ActionResult executeStrategy(
			@Param(value=StrategyTrend.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id) {
		var strategy = this.strategyService.findById(strategy_id);
		var invest = this.investService.findById(strategy.getInvestId());
		//判定买入
		var price = invest.getPrice();
		if(strategy.getBatchNum() == null) strategy.setBatchNum(1);
		final var batch_num = strategy.getBatchNum();
		var from_date = new Date();
		var to_date = DateUtils.add(from_date, Field.DATE, 5);
		//
		List<StrategyTrendDetail> list = this.trendDetailService.findByStrategyId(strategy_id).getRows();
		List<InvestmentSuggestion> suggestions = this.suggestionService.findByStrategyId(strategy_id, null).getRows();
		final StrategyTrendDetail fd = list.get(0);  //最后一档，倒序，取第一个
		if(list.size() > 1 && price >= fd.getSuggestBuyPrice() || list.size() == 1 && price <= fd.getSuggestBuyPrice()) { //满足买入条件
			boolean found = suggestions.stream().anyMatch(x -> 
				String.valueOf(fd.getDetailId()).equals(x.getExtra()) && 
				x.getTransactionType() == TransactionType.BUY.value() &&
				(x.getStatus() == InvestmentSuggestion.Status.ORDERED.value() || x.getStatus() == InvestmentSuggestion.Status.PREPARING.value())
			);
			if(!found && strategy.getStatus() == Strategy.Status.PROCESSING.value()) {
				String remarks = "当前价格：["+invest.getPrice()+"]，满足趋势交易价格："+ fd.getSuggestBuyPrice()+"买入设定，建议买入！";
				this.suggestionService.create(invest.getUserId(), invest.getInvestId(), strategy_id, batch_num + 1, invest.getAssetsName(), remarks, String.valueOf(fd.getDetailId()),
						TransactionType.BUY.value(), fd.getSuggestBuyAmount(), fd.getSuggestBuyQuantity(), from_date, to_date, null, "system", new Date());
			}
		} else {
			for(int i= 1; i <= list.size() - 1; i ++) {
				StrategyTrendDetail d = list.get(i);
				boolean found = suggestions.stream().anyMatch(x -> 
					String.valueOf(fd.getDetailId()).equals(x.getExtra()) && 
					x.getTransactionType() == TransactionType.SELL.value() &&
					(x.getStatus() == InvestmentSuggestion.Status.ORDERED.value() || x.getStatus() == InvestmentSuggestion.Status.PREPARING.value())
				);
				if(d.getBuyAmount() != null && d.getSellQuantity() == null && d.getSuggestSellPrice() != null && !found) {
					if(price < d.getSuggestSellPrice()) {
						String remarks = "当前价格：["+invest.getPrice()+"]，满足趋势交易价格："+ d.getSuggestSellPrice()+"卖出设定;建议卖出！";
						this.suggestionService.create(invest.getUserId(), invest.getInvestId(), strategy_id, batch_num + 1, invest.getAssetsName(), remarks, String.valueOf(d.getDetailId()),
								TransactionType.SELL.value(), d.getBuyQuantity() * price, d.getBuyQuantity(), from_date, to_date, null, "system", new Date());
					}
				}
			}
		}
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}
	
	@Override
	@Transaction
	public ActionResult flushDetails(
			@Param(value=StrategyTrend.STRATEGY_ID, required=true,	desc="策略ID") Long strategy_id) {
		Strategy s = this.strategyService.findById(strategy_id);
		Investment v = this.investService.findById(s.getInvestId());
		List<StrategyTrendDetail> list = this.trendDetailService.findByStrategyId(strategy_id).getRows();
		if(list.isEmpty()) {
			StrategyTrend trend = this.findById(strategy_id);
			Double price = trend.getPriceBenchmark();
			
			Invest buy = new Invest(trend.getAmount(), price, 100.0);
			if(trend.getCommissionType() == CommissionType.QUANTITY.value()) {
				buy.setQuantity(trend.getQuantity());
				buy.setAmount(buy.getQuantity() * price);
			}
			//
			this.trendDetailService.create(s.getUserId(), s.getTypeId(), s.getInvestId(), strategy_id, 
					1, 1.0, price, buy.getAmount(), buy.getQuantity());
		}
		list.stream().filter(d -> d.getSellQuantity() == null && d.getBuyQuantity() != null).forEach(d -> {
			this.trendDetailService.updateProfitInfo(d.getDetailId(), v.getPrice(), d.getBuyQuantity() * (v.getPrice() - d.getBuyPrice()));
		});
		return ModelFactory.create(ActionResult.class, Codes.CODE, Codes.SUCCESS);
	}
}